package com.spukmk3me.scenepack.pack0;

import com.spukmk3me.scene.Node;
import com.spukmk3me.math.Matrix4f;
import com.spukmk3me.math.Rectangle;

/* $if SPUKMK3ME_DEBUG$ */
import com.spukmk3me.debug.Logger;
/* $endif$ */

public final class DefaultNode extends Node
{
    DefaultNode( String name )
    {
        m_name      = name;
        c_matrix    = new Matrix4f();
        m_clipping  = new Rectangle();
        
        setFlags( VISIBLE | ENABLE | ANIMATE | USEBLENDING );
        m_planeColor = 0xFFFFFFFF;
    }

    @Override
    public String getName()
    {
        return m_name;
    }
    
    @Override
    public Node next()
    {
        if ( c_next == c_parent.c_children )
            return c_parent.c_children.c_next;
        
        return c_next;
    }
    
    @Override
    public Node prev()
    {
        if ( c_prev == c_parent.c_children )
            return c_parent.c_children.c_prev;
        
        return c_prev;
    }
    
    @Override
    public Node parent()
    {
        return c_parent;
    }
    
    @Override
    public Node child()
    {
        if ( c_children == null )
            return null;
        
        return c_children.c_next;
    }
    
    @Override
    public boolean isFirstSibling()
    {
        if ( c_parent == null )
            return true;
        
        return c_prev == c_parent.c_children;
    }
    
    @Override
    public boolean isLastSibling()
    {
        if ( c_parent == null )
            return true;
        
        return c_next == c_parent.c_children;
    }
    
    @Override
    public boolean hasChildren()
    {
        return c_children != null;
    }
    
    @Override
    public boolean hasParent()
    {
        return c_parent != null;
    }
    
    @Override
    public void setFlags( int flags )
    {
        m_flags = flags;
    }
    
    @Override
    public int getFlags()
    {
        return m_flags;
    }
    
    @Override
    public void setVisible( boolean visible )
    {
        if ( visible )
            m_flags |= VISIBLE;
        else
            m_flags &= ~VISIBLE;
    }
    
    @Override
    public boolean getVisible()
    {
        return (m_flags & VISIBLE) != 0;
    }
    
    @Override
    public void setEnable( boolean enable )
    {
        if ( enable )
            m_flags |= ENABLE;
        else
            m_flags &= ~ENABLE;
    }
    
    @Override
    public boolean getEnable()
    {
        return (m_flags & ENABLE) != 0;
    }
    
    @Override
    public void setAnimating( boolean enable )
    {
        if ( enable )
            m_flags |= ANIMATE;
        else
            m_flags &= ~ANIMATE;
    }
    
    @Override
    public boolean getAnimating()
    {
        return (m_flags & ANIMATE) != 0;
    }
    
    @Override
    public void setBlendingUsage( boolean useBlending )
    {
        if ( useBlending )
            m_flags |= USEBLENDING;
        else
            m_flags &= ~USEBLENDING;
    }
    
    @Override
    public boolean getBlendingUsage()
    {
        return (m_flags & USEBLENDING) != 0;
    }
    
    @Override
    public void setClippingUsage( boolean useClip )
    {
        if ( useClip )
            m_flags |= USECLIPPING;
        else
            m_flags &= ~USECLIPPING;
    }
    
    @Override
    public boolean getClippingUsage()
    {
        return (m_flags & USECLIPPING) != 0;
    }
    
    @Override
    public void getClip( Rectangle clipData )
    {
        clipData.copy( m_clipping );
    }
    
    @Override
    public void setClip( Rectangle clipData )
    {
        m_clipping.copy( clipData );
    }

    @Override
    public void setClip( int x, int y, int w, int h )
    {
        m_clipping.set( x, y, w, h ); 
    }
    
    @Override
    public void setPlaneColor( int color )
    {
        m_planeColor = color;
    }
    
    @Override
    public int getPlaneColor()
    {
        return m_planeColor; 
    }
    
    @Override
    public void setAdditionalColor( int color )
    {
        m_additionalColor = color;
    }

    @Override
    public int getAdditionalColor()
    {
        return m_additionalColor;
    }
    
    @Override
    public void t( float x, float y, float z )
    {
        c_matrix.t( x, y, z );
    }
    
    @Override
    public void s( float sx, float sy, float sz )
    {
        c_matrix.s( sx, sy, sz );
    }
    
    @Override
    public void r( float vx, float vy, float vz, float angle )
    {
        c_matrix.r( vx, vy, vz, angle );
    }
    
    @Override
    public void c( Matrix4f m )
    {
        c_matrix.copy( m );
    }
    
    @Override
    public void reset()
    {
        c_matrix.reset();
    }
    
    @Override
    public void getMatrix( Matrix4f m )
    {
        m.copy( c_matrix );
    }
    
    void addChild( DefaultNode node )
    {
        /* $if SPUKMK3ME_DEBUG$ */
        if ( (node.c_next != null) || (node.c_prev != null) ||
            (node.c_parent != null) )
            Logger.trace( "Adding potentially defected node." );
        /* $endif$ */

        DefaultNode bindingNode;

        if ( c_children == null )
        {
            c_children              = bindingNode = new DefaultNode( null );
            bindingNode.c_parent    = this;
            bindingNode.c_prev      = bindingNode.c_next = bindingNode;
        }
        else
            bindingNode = c_children;

        node.c_prev = bindingNode.c_prev;
        node.c_next = bindingNode;
        bindingNode.c_prev.c_next   = node;
        bindingNode.c_prev          = node;
        node.c_parent               = this;
    }

    /**
     *  Insert a node after another.
     *  @param node The node that is going to be added.
     *  @param predecessor The node that is supposed to lie right before the
     * added node.
     */
    void insertAfter( DefaultNode predecessor )
    {
        detach();
        
        c_prev = predecessor;
        c_next = predecessor.c_next;
        predecessor.c_next.c_prev   = this;
        predecessor.c_next          = this;
        c_parent = predecessor.c_parent;
    }
    
    /**
     *  Remove this node from its direct father and sibling list.
     *  \details All of its children are retained.
     */
    void detach()
    {
        // This node doesn't have a parent
        if ( (c_prev == null) || (c_next == null) || (c_parent == null) )
            return;
        
        c_prev.c_next = c_next;
        c_next.c_prev = c_prev;

        if ( c_prev == c_next ) // faked-first node found
        {
            DefaultNode faked_first = c_next;
            
            if ( c_parent != null )
                c_parent.c_children = null;

            faked_first.c_next = faked_first.c_prev = faked_first.c_parent = null;
        }

        c_next = c_prev = c_parent = null;
    }
    
    /**
     *  Detach all children.
     */
    void detachChildren()
    {
        if ( c_children == null )
            return;

        DefaultNode finishNode   = c_children;
        DefaultNode iterator     = finishNode.c_next;
        
        while ( iterator != finishNode )
        {
            iterator = iterator.c_next;
            iterator.c_prev.detach();
        }
    }
    
    DefaultNode c_prev;     //!< Previous node.
    DefaultNode c_next;     //!< Next node.
    DefaultNode c_children; //!< The faked first node of direct children.
    DefaultNode c_parent;   //!< The direct father of this node.
    Matrix4f    c_matrix;
    
    private final String    m_name;
    private Rectangle       m_clipping;
    private int             m_flags, m_planeColor, m_additionalColor;;
}